package pe.com.casadesign.dao.impl;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;

import javax.sql.DataSource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import org.springframework.jdbc.core.simple.SimpleJdbcCall;
import org.springframework.stereotype.Repository;

import pe.com.casadesign.dao.TransporteDAO;
import pe.com.casadesign.domain.ConductorTransporte;
import pe.com.casadesign.domain.ListaConductorTransporte;
import pe.com.casadesign.domain.ListaTransporte;
import pe.com.casadesign.domain.ListaVehiculoTransporte;
import pe.com.casadesign.domain.Transporte;
import pe.com.casadesign.domain.VehiculoTransporte;

/**
 * Implementacion de TransporteDAO.
 * 
 * @author Pedro Vera
 * 
 */
@Repository
public class TransporteDAOImpl implements TransporteDAO {
	private static final Logger logger = LoggerFactory
			.getLogger(TransporteDAOImpl.class);

	// Nombre Procedimientos Almacenados
	private static String SP_LISTAR_TRANSPORTE = "SP_LISTAR_TRANSPORTE";
	private static String SP_OBTENER_TRANSPORTE = "SP_OBTENER_TRANSPORTE";
	private static String SP_REGISTRAR_TRANSPORTE = "SP_REGISTRAR_TRANSPORTE";
	private static String SP_MODIFICAR_TRANSPORTE = "SP_MODIFICAR_TRANSPORTE";
	private static String SP_ELIMINAR_TRANSPORTE = "SP_ELIMINAR_TRANSPORTE";

	private static String SP_REGISTRAR_VEHICULO_TRANSPORTE = "SP_REGISTRAR_VEHICULO_TRANSPORTE";
	private static String SP_LIMPIAR_VEHICULO_TRANSPORTE = "SP_LIMPIAR_VEHICULO_TRANSPORTE";

	private static String SP_REGISTRAR_CONDUCTOR_TRANSPORTE = "SP_REGISTRAR_CONDUCTOR_TRANSPORTE";
	private static String SP_LIMPIAR_CONDUCTOR_TRANSPORTE = "SP_LIMPIAR_CONDUCTOR_TRANSPORTE";

	// Parametros de Entrada
	private static String PARAM_CODTRANSPORTE = "p_codtransporte";
	private static String PARAM_RAZONSOCIAL = "p_razonsocial";
	private static String PARAM_RUC = "p_ruc";
	private static String PARAM_ESTADO = "p_estado";

	private static String PARAM_VEHICULO_PLACA = "p_placa";
	private static String PARAM_VEHICULO_MODELO = "p_modelo";
	private static String PARAM_VEHICULO_MARCA = "p_marca";

	private static String PARAM_CONDUCTOR_NOMBRESYAPELLIDOS = "p_nombresyapellidos";
	private static String PARAM_CONDUCTOR_LICENCIA = "p_licencia";
	private static String PARAM_CONDUCTOR_DNI = "p_dni";

	// Parametros de Salida
	private static String CODTRANSPORTE = "CODTRANSPORTE";
	private static String RAZONSOCIAL = "RAZONSOCIAL";
	private static String RUC = "RUC";
	private static String ESTADO = "ESTADO";
	private static String FECHAREGISTRO = "FECHAREGISTRO";

	private static String VEHICULO_PLACA = "PLACA";
	private static String VEHICULO_MODELO = "MODELO";
	private static String VEHICULO_MARCA = "MARCA";

	private static String CONDUCTOR_NOMBRESYAPELLIDOS = "NOMBRESYAPELLIDOS";
	private static String CONDUCTOR_LICENCIA = "LICENCIA";
	private static String CONDUCTOR_DNI = "DNI";

	private static String LISTADO = "LISTADO";
	private static String LISTADO_VEHICULOS = "LISTADO_VEHICULOS";
	private static String LISTADO_CONDUCTORES = "LISTADO_CONDUCTORES";

	private static String TOTAL_FILAS_AFECTADAS = "#update-count-1";

	private JdbcTemplate jdbcTemplate;

	@Autowired
	public void setDataSource(DataSource dataSource) {
		this.jdbcTemplate = new JdbcTemplate(dataSource);
	}

	@Override
	public ListaTransporte listarTransporte(Transporte transporte) {
		logger.info("listarTransporte");

		// Instanciando variable para llamado de SP
		SimpleJdbcCall llamarSP = new SimpleJdbcCall(jdbcTemplate)
				.withProcedureName(SP_LISTAR_TRANSPORTE).returningResultSet(
						LISTADO, new RowMapper<Transporte>() {
							@Override
							public Transporte mapRow(final ResultSet rs,
									final int numRow) throws SQLException {
								final Transporte transporte = new Transporte();
								transporte.setCodTransporte(rs
										.getInt(CODTRANSPORTE));
								transporte.setRazonSocial(rs
										.getString(RAZONSOCIAL));
								transporte.setRuc(rs.getString(RUC));
								transporte.setEstado(rs.getString(ESTADO));
								transporte.setFechaRegistro(rs.getDate(
										FECHAREGISTRO).toString());
								return transporte;
							}
						});

		// Estableciendo parametros de entrada al SP
		SqlParameterSource parametros = new MapSqlParameterSource()
				.addValue(PARAM_RUC, transporte.getRuc())
				.addValue(PARAM_RAZONSOCIAL, transporte.getRazonSocial())
				.addValue(PARAM_ESTADO, transporte.getEstado());

		// Establecer parametros y ejecutar SP
		Map<String, Object> data = llamarSP.execute(parametros);

		// Obteniendo lista generada de ejecutar SP
		@SuppressWarnings("unchecked")
		List<Transporte> listaTransporte = (List<Transporte>) data.get(LISTADO);

		// Instanciando objeto listaTransporte
		ListaTransporte listaTransporteResultado = new ListaTransporte();
		// Estableciendo resultado al objeto listaTransporteResultado
		listaTransporteResultado.setLista(listaTransporte);

		if (logger.isDebugEnabled() && listaTransporteResultado != null) {
			logger.debug("size=" + listaTransporteResultado.size());
		}
		return listaTransporteResultado;
	}

	@Override
	public Transporte obtenerTransporte(Transporte transporte) {
		logger.info("obtenerTransporte");

		// Instanciando variable para llamado de SP
		SimpleJdbcCall llamarSP = new SimpleJdbcCall(jdbcTemplate)
				.withProcedureName(SP_OBTENER_TRANSPORTE)
				.returningResultSet(LISTADO, new RowMapper<Transporte>() {
					@Override
					public Transporte mapRow(final ResultSet rs,
							final int numRow) throws SQLException {
						final Transporte transporte = new Transporte();
						transporte.setCodTransporte(rs.getInt(CODTRANSPORTE));
						transporte.setRazonSocial(rs.getString(RAZONSOCIAL));
						transporte.setRuc(rs.getString(RUC));
						transporte.setEstado(rs.getString(ESTADO));
						transporte.setFechaRegistro(rs.getDate(FECHAREGISTRO)
								.toString());
						return transporte;
					}
				})
				.returningResultSet(LISTADO_VEHICULOS,
						new RowMapper<VehiculoTransporte>() {
							@Override
							public VehiculoTransporte mapRow(
									final ResultSet rs, final int numRow)
									throws SQLException {
								final VehiculoTransporte vehiculoTransporte = new VehiculoTransporte();
								vehiculoTransporte.setPlaca(rs
										.getString(VEHICULO_PLACA));
								vehiculoTransporte.setModelo(rs
										.getString(VEHICULO_MODELO));
								vehiculoTransporte.setMarca(rs
										.getString(VEHICULO_MARCA));
								return vehiculoTransporte;
							}
						})
				.returningResultSet(LISTADO_CONDUCTORES,
						new RowMapper<ConductorTransporte>() {
							@Override
							public ConductorTransporte mapRow(
									final ResultSet rs, final int numRow)
									throws SQLException {
								final ConductorTransporte conductorTransporte = new ConductorTransporte();
								conductorTransporte.setNombresyApellidos(rs
										.getString(CONDUCTOR_NOMBRESYAPELLIDOS));
								conductorTransporte.setLicencia(rs
										.getString(CONDUCTOR_LICENCIA));
								conductorTransporte.setDni(rs
										.getString(CONDUCTOR_DNI));
								return conductorTransporte;
							}
						});

		// Estableciendo parametros de entrada al SP
		SqlParameterSource parametros = new MapSqlParameterSource().addValue(
				PARAM_CODTRANSPORTE, transporte.getCodTransporte());

		// Establecer parametros y ejecutar SP
		Map<String, Object> data = llamarSP.execute(parametros);

		// Obteniendo lista generada de ejecutar SP
		@SuppressWarnings("unchecked")
		List<Transporte> listaTransporte = (List<Transporte>) data.get(LISTADO);

		// Declarando objeto transporte Obtenido
		Transporte transporteObtenido = null;

		if (logger.isDebugEnabled() && listaTransporte != null) {
			logger.debug("size=" + listaTransporte.size());
		}

		if (listaTransporte != null && listaTransporte.size() > 0) {
			// Obteniendo el primer elemento de la lista de transporte
			transporteObtenido = listaTransporte.get(0);

			// Obteniendo lista de vehiculos generada de ejecutar SP
			@SuppressWarnings("unchecked")
			List<VehiculoTransporte> listaVehiculoTransporte = (List<VehiculoTransporte>) data
					.get(LISTADO_VEHICULOS);

			// Instanciando objeto ListaVehiculoTransporte
			ListaVehiculoTransporte listaVehiculoTransporteResultado = new ListaVehiculoTransporte();
			// Estableciendo resultado al objeto
			// listaVehiculoTransporteResultado
			listaVehiculoTransporteResultado.setLista(listaVehiculoTransporte);

			// Asignando lista de vehiculos al objeto transporte creado
			transporteObtenido
					.setListaVehiculoTransporte(listaVehiculoTransporteResultado);

			if (logger.isDebugEnabled() && listaVehiculoTransporte != null) {
				logger.debug("sizeVehiculos=" + listaVehiculoTransporte.size());
			}

			// Obteniendo lista de conductores generada de ejecutar SP
			@SuppressWarnings("unchecked")
			List<ConductorTransporte> listaConductorTransporte = (List<ConductorTransporte>) data
					.get(LISTADO_CONDUCTORES);

			// Instanciando objeto ListaVehiculoTransporte
			ListaConductorTransporte listaConductorTransporteResultado = new ListaConductorTransporte();
			// Estableciendo resultado al objeto
			// listaConductorTransporteResultado
			listaConductorTransporteResultado
					.setLista(listaConductorTransporte);

			// Asignando lista de vehiculos al objeto transporte creado
			transporteObtenido
					.setListaConductorTransporte(listaConductorTransporteResultado);

			if (logger.isDebugEnabled() && listaConductorTransporte != null) {
				logger.debug("sizeConductores="
						+ listaConductorTransporte.size());
			}
		}
		return transporteObtenido;
	}

	@Override
	public boolean registrarTransporte(Transporte transporte) {
		logger.info("registrarTransporte");

		int resultado = 0;

		// Instanciando variable para llamado de SP
		SimpleJdbcCall llamarSP = new SimpleJdbcCall(jdbcTemplate)
				.withProcedureName(SP_REGISTRAR_TRANSPORTE);

		// Estableciendo parametros de entrada al SP
		SqlParameterSource parametros = new MapSqlParameterSource()
				.addValue(PARAM_RAZONSOCIAL, transporte.getRazonSocial())
				.addValue(PARAM_RUC, transporte.getRuc())
				.addValue(PARAM_ESTADO, transporte.getEstado());

		// Establecer parametros y ejecutar SP
		Map<String, Object> data = llamarSP.execute(parametros);

		// Obteniendo el total de filas afectadas al ejecutar SP
		resultado += (Integer) data.get(TOTAL_FILAS_AFECTADAS);

		logger.debug("codigo generado:" + data.get(PARAM_CODTRANSPORTE));

		// Obteniendo codigo de transporte
		int codigoTransporte = (Integer) data.get(PARAM_CODTRANSPORTE);

		// Obteniendo lista de vehiculos a agregar
		ListaVehiculoTransporte listaVehiculoTransporte = transporte
				.getListaVehiculoTransporte();

		// Si existen vehiculos entonces registrarlos
		if (listaVehiculoTransporte.getLista() != null) {
			// Instanciando variable para llamado de SP
			SimpleJdbcCall llamarSPVehiculos = new SimpleJdbcCall(jdbcTemplate)
					.withProcedureName(SP_REGISTRAR_VEHICULO_TRANSPORTE);

			for (VehiculoTransporte item : listaVehiculoTransporte.getLista()) {
				// Estableciendo parametros de entrada al SP
				SqlParameterSource parametrosVehiculos = new MapSqlParameterSource()
						.addValue(PARAM_CODTRANSPORTE, codigoTransporte)
						.addValue(PARAM_VEHICULO_PLACA, item.getPlaca())
						.addValue(PARAM_VEHICULO_MODELO, item.getModelo())
						.addValue(PARAM_VEHICULO_MARCA, item.getMarca());

				// Establecer parametros y ejecutar SP
				Map<String, Object> dataVehiculo = llamarSPVehiculos
						.execute(parametrosVehiculos);

				// Obteniendo el total de filas afectadas al ejecutar SP
				resultado += (Integer) dataVehiculo.get(TOTAL_FILAS_AFECTADAS);
			}
		}

		// Obteniendo lista de conductor a agregar
		ListaConductorTransporte listaConductorTransporte = transporte
				.getListaConductorTransporte();

		// Si existen conductores entonces registrarlos
		if (listaVehiculoTransporte.getLista() != null) {
			// Instanciando variable para llamado de SP
			SimpleJdbcCall llamarSPConductores = new SimpleJdbcCall(
					jdbcTemplate)
					.withProcedureName(SP_REGISTRAR_CONDUCTOR_TRANSPORTE);

			for (ConductorTransporte item : listaConductorTransporte.getLista()) {
				// Estableciendo parametros de entrada al SP
				SqlParameterSource parametrosConductores = new MapSqlParameterSource()
						.addValue(PARAM_CODTRANSPORTE, codigoTransporte)
						.addValue(PARAM_CONDUCTOR_NOMBRESYAPELLIDOS,
								item.getNombresyApellidos())
						.addValue(PARAM_CONDUCTOR_LICENCIA, item.getLicencia())
						.addValue(PARAM_CONDUCTOR_DNI, item.getDni());

				// Establecer parametros y ejecutar SP
				Map<String, Object> dataConductor = llamarSPConductores
						.execute(parametrosConductores);

				// Obteniendo el total de filas afectadas al ejecutar SP
				resultado += (Integer) dataConductor.get(TOTAL_FILAS_AFECTADAS);
			}
		}

		return resultado > 0;
	}

	@Override
	public boolean modificarTransporte(Transporte transporte) {
		logger.info("modificarTransporte");

		int resultado = 0;

		// Instanciando variable para llamado de SP
		SimpleJdbcCall llamarSP = new SimpleJdbcCall(jdbcTemplate)
				.withProcedureName(SP_MODIFICAR_TRANSPORTE);

		// Estableciendo parametros de entrada al SP
		SqlParameterSource parametros = new MapSqlParameterSource()
				.addValue(PARAM_CODTRANSPORTE, transporte.getCodTransporte())
				.addValue(PARAM_RAZONSOCIAL, transporte.getRazonSocial())
				.addValue(PARAM_RUC, transporte.getRuc())
				.addValue(PARAM_ESTADO, transporte.getEstado());

		// Establecer parametros y ejecutar SP
		Map<String, Object> data = llamarSP.execute(parametros);

		// Obteniendo el total de filas afectadas al ejecutar SP
		resultado += (Integer) data.get(TOTAL_FILAS_AFECTADAS);

		// Instanciando variable para llamado de SP Eliminar vehiculos
		SimpleJdbcCall llamarSPLimpiarVehiculos = new SimpleJdbcCall(
				jdbcTemplate).withProcedureName(SP_LIMPIAR_VEHICULO_TRANSPORTE);
		// Agregar parametro codigo de proveedor
		llamarSPLimpiarVehiculos.execute(new MapSqlParameterSource().addValue(
				PARAM_CODTRANSPORTE, transporte.getCodTransporte()));

		// Obteniendo lista de vehiculos a agregar
		ListaVehiculoTransporte listaVehiculoTransporte = transporte
				.getListaVehiculoTransporte();

		// Si existen vehiculos entonces registrarlos
		if (listaVehiculoTransporte.getLista() != null) {
			// Instanciando variable para llamado de SP
			SimpleJdbcCall llamarSPVehiculos = new SimpleJdbcCall(jdbcTemplate)
					.withProcedureName(SP_REGISTRAR_VEHICULO_TRANSPORTE);

			for (VehiculoTransporte item : listaVehiculoTransporte.getLista()) {
				// Estableciendo parametros de entrada al SP
				SqlParameterSource parametrosVehiculos = new MapSqlParameterSource()
						.addValue(PARAM_CODTRANSPORTE,
								transporte.getCodTransporte())
						.addValue(PARAM_VEHICULO_PLACA, item.getPlaca())
						.addValue(PARAM_VEHICULO_MODELO, item.getModelo())
						.addValue(PARAM_VEHICULO_MARCA, item.getMarca());

				// Establecer parametros y ejecutar SP
				Map<String, Object> dataVehiculo = llamarSPVehiculos
						.execute(parametrosVehiculos);

				// Obteniendo el total de filas afectadas al ejecutar SP
				resultado += (Integer) dataVehiculo.get(TOTAL_FILAS_AFECTADAS);
			}
		}

		// Instanciando variable para llamado de SP Eliminar conductores
		SimpleJdbcCall llamarSPLimpiarConductores = new SimpleJdbcCall(
				jdbcTemplate)
				.withProcedureName(SP_LIMPIAR_CONDUCTOR_TRANSPORTE);
		// Agregar parametro codigo de proveedor
		llamarSPLimpiarConductores.execute(new MapSqlParameterSource()
				.addValue(PARAM_CODTRANSPORTE, transporte.getCodTransporte()));

		// Obteniendo lista de conductor a agregar
		ListaConductorTransporte listaConductorTransporte = transporte
				.getListaConductorTransporte();

		// Si existen conductores entonces registrarlos
		if (listaVehiculoTransporte.getLista() != null) {
			// Instanciando variable para llamado de SP
			SimpleJdbcCall llamarSPConductores = new SimpleJdbcCall(
					jdbcTemplate)
					.withProcedureName(SP_REGISTRAR_CONDUCTOR_TRANSPORTE);

			for (ConductorTransporte item : listaConductorTransporte.getLista()) {
				// Estableciendo parametros de entrada al SP
				SqlParameterSource parametrosConductores = new MapSqlParameterSource()
						.addValue(PARAM_CODTRANSPORTE,
								transporte.getCodTransporte())
						.addValue(PARAM_CONDUCTOR_NOMBRESYAPELLIDOS,
								item.getNombresyApellidos())
						.addValue(PARAM_CONDUCTOR_LICENCIA, item.getLicencia())
						.addValue(PARAM_CONDUCTOR_DNI, item.getDni());

				// Establecer parametros y ejecutar SP
				Map<String, Object> dataConductor = llamarSPConductores
						.execute(parametrosConductores);

				// Obteniendo el total de filas afectadas al ejecutar SP
				resultado += (Integer) dataConductor.get(TOTAL_FILAS_AFECTADAS);
			}
		}

		return resultado > 0;
	}

	@Override
	public boolean eliminarTransporte(Transporte transporte) {
		logger.info("eliminarTransporte");

		// Instanciando variable para llamado de SP
		SimpleJdbcCall llamarSP = new SimpleJdbcCall(jdbcTemplate)
				.withProcedureName(SP_ELIMINAR_TRANSPORTE);

		// Estableciendo parametros de entrada al SP
		SqlParameterSource parametros = new MapSqlParameterSource().addValue(
				PARAM_CODTRANSPORTE, transporte.getCodTransporte());

		// Establecer parametros y ejecutar SP
		Map<String, Object> data = llamarSP.execute(parametros);

		// Obteniendo el total de filas afectadas al ejecutar SP
		int totalRegistrosAfectado = (Integer) data.get(TOTAL_FILAS_AFECTADAS);

		return totalRegistrosAfectado > 0;
	}

}
